using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using EnterpriseDT.Net.Ftp;

using ICSharpCode.SharpZipLib.GZip;

namespace StormModelToKMLDB
{
    public class SSTGetter
    {
        #region Events
        public delegate void SSTDoneHandler();
        public delegate void CurrentSSTHandler(TempBitmap bm);
        public delegate void CurrentSST7DeltaHandler(TempBitmap bm);
        public delegate void CurrentSST3DeltaHandler(TempBitmap bm);
        public event SSTDoneHandler SSTDone;
        public event CurrentSSTHandler CurrentSSTRetrieved;
        public event CurrentSST7DeltaHandler CurrentSST7DeltaRetrieved;
        public event CurrentSST3DeltaHandler CurrentSST3DeltaRetrieved;
        public event LogDelegate LogMessage;
        #endregion

        #region Member variables
        /// <summary>
        /// Thread that polls discover-earth ftp for new data, and subsequently builds Tempmaps from the data.
        /// </summary>
        private Thread _getSSTThread;
        /// <summary>
        /// The ftp client that connects to the NHC server and retrieves the raw data
        /// </summary>
        private FTPClient _ftp;
        /// <summary>
        /// Time delta between ftp polls for NHC data
        /// </summary>
        private int _sleepMillis;
        /// <summary>
        /// Byte array to store raw data.
        /// </summary>
        //private byte[] _bytes;// = new byte[1036800];
        //private double _minTemp = 100;
        //private double _maxTemp = 0;
        //private double _step = 35.0 / 1275.0;
        #endregion

        #region Constructor
        public SSTGetter(int sleepMinutes)
        {
            this._sleepMillis = sleepMinutes * 60 * 1000;

            _ftp = new FTPClient();

            // Set up Get Thread
            this._getSSTThread = new Thread(new ThreadStart(this.RunGetThread));
            this._getSSTThread.IsBackground = true;
            this._getSSTThread.Name = "SSTGetterThread";
            
        }
        #endregion

        #region Public Methods
        public void Start()
        {
            if (!this._getSSTThread.IsAlive)
            {
                this._getSSTThread = new Thread(new ThreadStart(this.RunGetThread));
                this._getSSTThread.IsBackground = true;
                this._getSSTThread.Name = "SSTGetterThread";
                this._getSSTThread.Start();
            }
        }
        public void Stop()
        {
            if(this._getSSTThread != null)
                this._getSSTThread.Interrupt();
            try
            {
                this._ftp.QuitImmediately();
            }
            catch (Exception)
            {
                return;
            }
        }

        #endregion

        #region Thread Method
        private void RunGetThread()
        {
            while (true)
            {
                try
                {
                    lock (_ftp)
                    {
                        #region Connect
                        _ftp.RemoteHost = "ftp.discover-earth.org";
                        _ftp.Connect();
                        _ftp.Timeout = 30000;
                        _ftp.Login("anonymous", "me@home.com");
                        _ftp.ConnectMode = FTPConnectMode.PASV;
                        _ftp.TransferType = FTPTransferType.ASCII;
                        _ftp.ChDir("/sst/daily/tmi_amsre");
                        #endregion

                        #region Check Connection
                        if (!_ftp.Connected)
                        {
                            Thread.Sleep(5000);
                            throw new Exception();
                        }
                        #endregion

                        #region Get neccessary SST GRIBs
                        // Get latest final sst
                        DateTime latest = this.GetLatestFinal(_ftp.Dir());
                        string target = this.GetDayOfYearFileName(latest);
                        Log("SST: Retrieved " + target + " as latest final sst product");

                        // Get 3 days ago
                        string target3 = this.GetDayOfYearFileName(latest, 3);

                        // Get 7 days ago
                        string target7 = this.GetDayOfYearFileName(latest, 7);

                        //FTPFile[] detls = _ftp.DirDetails(".");
                        //FTPFile f = null;
                        //f = detls[detls.Length-1];
                        #endregion

                        #region Parse SST Files and fire Events
                        byte[] b = this.GetSSTFile(target);
                        TempBitmap tbm_1 = new TempBitmap(b, 0, -90, 90, target);  
                        TempBitmap tbm_2 = new TempBitmap(b, 0, -180, 90, target); 
                        TempBitmap tbm_3 = new TempBitmap(b, -90, -90, 90, target);  
                        TempBitmap tbm_4 = new TempBitmap(b, -90, -180, 90, target);
                        TempBitmap tbm_5 = new TempBitmap(b, 0, 0, 90, target);
                        TempBitmap tbm_6 = new TempBitmap(b, 0, 90, 90, target);
                        TempBitmap tbm_7 = new TempBitmap(b, -90, 0, 90, target);
                        TempBitmap tbm_8 = new TempBitmap(b, -90, 90, 90, target); 

                        b = this.GetSSTFile(target3);
                        TempBitmap tbm3_1 = new TempBitmap(b, 0, -90, 90, target3);
                        TempBitmap tbm3_2 = new TempBitmap(b, 0, -180, 90, target3);
                        TempBitmap tbm3_3 = new TempBitmap(b, -90, -90, 90, target3);
                        TempBitmap tbm3_4 = new TempBitmap(b, -90, -180, 90, target3);
                        TempBitmap tbm3_5 = new TempBitmap(b, 0, 0, 90, target3);
                        TempBitmap tbm3_6 = new TempBitmap(b, 0, 90, 90, target3);
                        TempBitmap tbm3_7 = new TempBitmap(b, -90, 0, 90, target3);
                        TempBitmap tbm3_8 = new TempBitmap(b, -90, 90, 90, target3);

                        b = this.GetSSTFile(target7);
                        TempBitmap tbm7_1 = new TempBitmap(b, 0, -90, 90, target7);
                        TempBitmap tbm7_2 = new TempBitmap(b, 0, -180, 90, target7);
                        TempBitmap tbm7_3 = new TempBitmap(b, -90, -90, 90, target7);
                        TempBitmap tbm7_4 = new TempBitmap(b, -90, -180, 90, target7);
                        TempBitmap tbm7_5 = new TempBitmap(b, 0, 0, 90, target7);
                        TempBitmap tbm7_6 = new TempBitmap(b, 0, 90, 90, target7);
                        TempBitmap tbm7_7 = new TempBitmap(b, -90, 0, 90, target7);
                        TempBitmap tbm7_8 = new TempBitmap(b, -90, 90, 90, target7);
                        
                        // Make the TempBitmaps and throw the events
                        if (this.CurrentSSTRetrieved != null)
                        {
                            this.CurrentSSTRetrieved(tbm_1);
                            this.CurrentSSTRetrieved(tbm_2);
                            this.CurrentSSTRetrieved(tbm_3);
                            this.CurrentSSTRetrieved(tbm_4);
                            this.CurrentSSTRetrieved(tbm_5);
                            this.CurrentSSTRetrieved(tbm_6);
                            this.CurrentSSTRetrieved(tbm_7);
                            this.CurrentSSTRetrieved(tbm_8);
                        }
                        if (this.CurrentSST3DeltaRetrieved != null)
                        {
                            this.CurrentSST3DeltaRetrieved(tbm3_1);
                            this.CurrentSST3DeltaRetrieved(tbm3_2);
                            this.CurrentSST3DeltaRetrieved(tbm3_3);
                            this.CurrentSST3DeltaRetrieved(tbm3_4);
                            this.CurrentSST3DeltaRetrieved(tbm3_5);
                            this.CurrentSST3DeltaRetrieved(tbm3_6);
                            this.CurrentSST3DeltaRetrieved(tbm3_7);
                            this.CurrentSST3DeltaRetrieved(tbm3_8);
                        }
                        if (this.CurrentSST7DeltaRetrieved != null)
                        {
                            this.CurrentSST7DeltaRetrieved(tbm7_1);
                            this.CurrentSST7DeltaRetrieved(tbm7_2);
                            this.CurrentSST7DeltaRetrieved(tbm7_3);
                            this.CurrentSST7DeltaRetrieved(tbm7_4);
                            this.CurrentSST7DeltaRetrieved(tbm7_5);
                            this.CurrentSST7DeltaRetrieved(tbm7_6);
                            this.CurrentSST7DeltaRetrieved(tbm7_7);
                            this.CurrentSST7DeltaRetrieved(tbm7_8);
                        }
                        if (this.SSTDone != null)
                        {
                            this.SSTDone();
                        }
                        #endregion
                    }
                }
                catch (Exception e)
                {
                    Log(" SST: " + Thread.CurrentThread.Name + "\n" + e.Message);
                    Console.WriteLine(DateTime.Now.ToString() + " SST: " + Thread.CurrentThread.Name + "\n" + e.Message + "\n");
                }
                finally
                {
                    #region Close Connection
                    _ftp.Quit();
                    #endregion
                }

                try
                {
                    Thread.Sleep(this._sleepMillis);
                }
                catch (ThreadInterruptedException)
                {
                    return;
                }
            }
        }
        #endregion

        #region Private Helpers
        private void Log(string txt)
        {
            if (LogMessage != null)
                LogMessage(txt);
        }
        private DateTime GetLatestFinal(string[] files)
        {
            List<string> fNames = new List<string>(files);

            DateTime start = DateTime.Now;
            string year = start.Year.ToString();
            string doy = string.Format("{0:000}", start.DayOfYear.ToString());

            string qry = "tmi_amsre.fusion." + year + "." + doy + ".v02.gz";

            bool found = false;
            while (!found)
            {
                if (fNames.Contains(qry))
                    found = true;
                else
                {
                    start -= new TimeSpan(1, 0, 0, 0);
                    year = start.Year.ToString();
                    doy = string.Format("{0:000}", start.DayOfYear.ToString());
                    qry = "tmi_amsre.fusion." + year + "." + doy + ".v02.gz";
                }
            }

            return start;
        }

        private byte[] GetSSTFile(string fileName)
        {
            byte[] b = _ftp.Get(fileName);
            GZipInputStream gz = new GZipInputStream(new MemoryStream(b));
            MemoryStream data = new MemoryStream(1036800);
            int size = 5760;
            byte[] writeData = new byte[5760];
            while (true)
            {
                size = gz.Read(writeData, 0, size);
                if (size > 0)
                {
                    data.Write(writeData, 0, size);
                }
                else
                {
                    break;
                }
            }
            gz.Close();
            return data.ToArray();
        }

        private string GetDayOfYearFileName(DateTime start)
        {
            string year = start.Year.ToString();
            string doy = string.Format("{0:000}", start.DayOfYear.ToString());

            return "tmi_amsre.fusion." + year + "." + doy + ".v02.gz";
        }

        private string GetDayOfYearFileName(DateTime start, int negativeDaysFromNow)
        {
            DateTime then = start - new TimeSpan(negativeDaysFromNow, 0, 0, 0);
            string year = then.Year.ToString();
            string doy = string.Format("{0:000}", then.DayOfYear.ToString());

            return "tmi_amsre.fusion." + year + "." + doy + ".v02.gz";
        }
        #endregion
    }
}
